/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * *
 *            Copyright (C) 2018 Institute of Computing Technology, CAS
 *               Author : Han Shukai (email : hanshukai@ict.ac.cn)
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * *
 *         The kernel's entry, where most of the initialization work is done.
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * *
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this
 * software and associated documentation files (the "Software"), to deal in the Software
 * without restriction, including without limitation the rights to use, copy, modify,
 * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
 * persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * */

#include <common.h>
#include <os/irq.h>
#include <os/mm.h>
#include <os/sched.h>
#include <screen.h>
#include <sbi.h>
#include <stdio.h>
#include <os/time.h>
#include <os/syscall.h>
#include <os/futex.h>
#include <test.h>

#include <csr.h>

extern void ret_from_exception();
extern void printk_task1(void);
extern void __global_pointer$();
list_node_t ready_list;

static void init_pcb_stack(
    ptr_t kernel_stack, ptr_t user_stack, ptr_t entry_point,
    pcb_t *pcb)
{
    regs_context_t *pt_regs =
        (regs_context_t *)(kernel_stack - sizeof(regs_context_t));
        pt_regs->sstatus=2;
        pt_regs->regs[1]=entry_point;
        pt_regs->regs[2]=user_stack;
        pt_regs->sepc=entry_point;
    regs_context_t *st_regs =
        (switchto_context_t *)(kernel_stack - sizeof(regs_context_t)-sizeof(switchto_context_t));
        st_regs->regs[0]=&ret_from_exception;
        st_regs->regs[1]=user_stack;
        st_regs->regs[2]=0;
        st_regs->regs[3]=0;
        st_regs->regs[4]=0;
        st_regs->regs[5]=0;
        st_regs->regs[6]=0;
        st_regs->regs[7]=0;
        st_regs->regs[8]=0;
        st_regs->regs[9]=0;
        st_regs->regs[10]=0;
        st_regs->regs[11]=0;
        st_regs->regs[12]=0;
        st_regs->regs[13]=0;
    /* TODO: initialization registers
     * note: sp, gp, ra, sepc, sstatus
     * gp should be __global_pointer$
     * To run the task in user mode,
     * you should set corresponding bits of sstatus(SPP, SPIE, etc.).
     */

    // set sp to simulate return from switch_to
    /* TODO: you should prepare a stack, and push some values to
     * simulate a pcb context.
     */
}

static void init_pcb()
{
     /* initialize all of your pcb and add them into ready_queue
     * TODO:
     */
    //pcb[NUM_MAX_TASK]是全局变量，初始化主要是要将pcb的kerne_stack和user_stack计算出来并在内存中分配。除此之外，
    //pcb数组和sched1_tasks和lock_tasks数组对应，任务数组是task_info结构体为元素，每个元素有entry_point和task_type两个域
    //entry_point是非常重要的域，指示进入此进程后指令的开始执行位置。初始化时就是进程代码的初始地址，如果发生过中断就是上次执行地址
    //这个地址会存在kernel栈的ra寄存器偏移量对应地址，调度到此进程后会从内存中将之拷贝到cpu的ra寄存器，然后jr ra跳转到该位置.
	init_list_head(&ready_queue);
	int i;
	for (i=0;i<3;i++){//num_sched1_task1为3而num_lock_tasks为2.进程1、2输出在屏幕的头两行，打印一段话并
			pcb[i].pid =process_id;  //输出执行次数。进程3是飞机。进程4和进程5完全一样，包含初始化lock,调度一次,打印等待语句获得lock
			process_id+=1;           //不断调度20次并打印正在使用lock的语句以及计数,释放lock并打印释放语句,调度。
                        //最终理想的结果是在屏幕的3、4行打印语句，两者语句永远不能相同。基本为一方打印Has acquired lock and running
			pcb[i].type=USER_PROCESS;  //和循环次数另一方为Applying for a lock。到20次时一方exit另一方放回ready_queue并且running
			pcb[i].status=TASK_READY;
			pcb[i].preempt_count=0;
			pcb[i].kernel_sp=allocPage(1);
			pcb[i].user_sp=allocPage(1);
			init_pcb_stack(pcb[i].kernel_sp,pcb[i].user_sp,sched2_tasks[i]->entry_point,&pcb[i]);
			list_add_tail(&pcb[i].list, &ready_queue);

	}
    
    for(;i<3+2;i++){
            pcb[i].pid =process_id;
            process_id+=1;
            pcb[i].type=KERNEL_THREAD;
            pcb[i].status=TASK_READY;
            pcb[i].preempt_count=0;
            pcb[i].rank=2;
            pcb[i].kernel_sp=allocPage(1);
            pcb[i].user_sp=allocPage(1);
            init_pcb_stack(pcb[i].kernel_sp,pcb[i].user_sp,lock2_tasks[i-3]->entry_point,&pcb[i]);
            list_add_tail(&pcb[i].list, &ready_queue);
    }
   for(;i<3+2+2;i++){
    		pcb[i].pid =process_id;
            process_id+=1;
            pcb[i].type=USER_PROCESS;
            pcb[i].status=TASK_READY;
            pcb[i].rank=1;
            pcb[i].preempt_count=0;
            pcb[i].kernel_sp=allocPage(1);
            pcb[i].user_sp=allocPage(1);
            init_pcb_stack(pcb[i].kernel_sp,pcb[i].user_sp,timer_tasks[i-3-2]->entry_point,&pcb[i]);
            list_add_tail(&pcb[i].list, &ready_queue);
    }

	current_running=&pid0_pcb;//pid0_pcb不会执行，第一次运行do_scheduler时轮换到pcb[0]
    //mutex_lock->lock.status=UNLOCKED;
    /* remember to initialize `current_running`
     * TODO:
     */
}
long illegal_syscall(void){
    printk("error:illegal_syscall a7 is not for any funtions we have");
}
static void init_syscall(void)
{
                                        /*extern long invoke_syscall(long, long, long, long);          
                                        #define IGNORE 0
                                        #define SYSCALL_SLEEP 2         void sys_sleep(uint32_t); 
                                        #define SYSCALL_FUTEX_WAIT 10   void sys_futex_wait(volatile uint64_t *val_addr, uint64_t val);
                                        #define SYSCALL_FUTEX_WAKEUP 11 void sys_futex_wakeup(volatile uint64_t *val_addr, int num_wakeup);
                                        #define SYSCALL_WRITE 20        void sys_write(char *);
                                        #define SYSCALL_READ 21
                                        #define SYSCALL_CURSOR 22       void sys_move_cursor(int, int);
                                        #define SYSCALL_REFLUSH 23      void sys_reflush(); 
                                        #define SYSCALL_GET_TIMEBASE 30 long sys_get_timebase();
                                        #define SYSCALL_GET_TICK 31     long sys_get_tick();
                                         写之前先做一个搬运找到所有的系统调用函数名字和在syscall函数指针数组的位置，只有本次实验要做的*/
    int i;
    for(i=0;i<64;i++)
        syscall[i]=&illegal_syscall;
    syscall[SYSCALL_SLEEP]=&do_sleep;
    syscall[1]=&do_scheduler;
    syscall[SYSCALL_FUTEX_WAIT]=&futex_wait;
    syscall[SYSCALL_FUTEX_WAKEUP]=&futex_wakeup;
    syscall[SYSCALL_WRITE]=&screen_write;//这里做个修改，以前是screen_write
    syscall[SYSCALL_CURSOR]=&screen_move_cursor;//以前是screen_move_cursor
    syscall[SYSCALL_REFLUSH]=&screen_reflush;
    syscall[SYSCALL_GET_TIMEBASE]=&get_time_base;
    syscall[SYSCALL_GET_TICK]=&get_ticks;
}

// jump from bootloader.
// The beginning of everything >_< ~~~~~~~~~~~~~~
int main()
{
    // init Process Control Block (-_-!)
    init_pcb();
    printk("> [INIT] PCB initialization succeeded.\n\r");

    
/*	
    // init futex mechanism
    init_system_futex();
*/
    //init interrupt (^_^)
    init_exception();
    printk("> [INIT] Interrupt processing initialization succeeded.\n\r");

    // init system call table (0_0)
    init_syscall();
    printk("> [INIT] System call initialized successfully.\n\r");

    // fdt_print(riscv_dtb);

    // init screen (QAQ)
    init_screen();
    printk("> [INIT] SCREEN initialization succeeded.\n\r");
    
    // TODO:
    // Setup timer interrupt and enable all interrupt
    enable_interrupt();
     time_base = sbi_read_fdt(TIMEBASE);
    // do_scheduler_non_ecall();
    //sbi_set_timer(get_ticks() + TIMER_INTERVAL);
     reset_irq_timer();
    //printk("reset_irq_timer succeeded\n\r");
    
    while (1) {
        // (QAQQQQQQQQQQQ)
        // If you do non-preemptive scheduling, you need to use it
        // to surrender control do_scheduler();
        //enable_interrupt();
        //user_do_scheduler();
         //__asm__ __volatile__("wfi\n\r":::);
     //
    }
    return 0;
}
